🎴

Pay-to-access via x402 on Filecoin Pay

Author: @Miroslav Bajtoš <miroslav@meridian.space>

Last updated:

This document is a rough dump of an idea for implementing x402 payments on Filecoin Pay and a pay-to-access scheme for direct retrievals from SPs.

Motivation

x402 is the hottest topic in Web3 right now — and we believe Filecoin, Filecoin Pay, and Filecoin Beam are uniquely positioned to become a foundational part of this emerging stack.

The x402 movement has reignited excitement around the long-forgotten HTTP 402 “Payment Required” status code, but with a modern twist. For the first time, we actually have the tools to make it real: crypto wallets, blockchains, and stablecoins. This convergence means we can finally turn the web into a true pay-as-you-go system for digital resources.

What’s more, x402 perfectly embodies the Web3 philosophy. By allowing clients to pay providers directly at the protocol layer, we minimize trust assumptions and enable peer-to-peer value exchange without intermediaries. It gives sovereignty back to users: instead of creating endless accounts, surrendering personal data, or trusting centralized payment processors, all you need is a wallet and enough balance to pay for what you consume. No accounts. No data leaks. No credit cards. Just autonomous access and ownership — powered by x402.

And we believe this is where Filecoin can shine. x402 is about paying to fetch a resource, but first, discovering who offers it at the best price through services like the x402 Bazaar. In the Filecoin ecosystem, we’ve been exploring this concept for years under the name Retrieval Markets.

Filecoin is a natural fit for x402:

  • Store the resource on the Filecoin Storage Network.
  • Retrieve it through Filecoin Beam.
  • Verify integrity with content addressing.
  • Pay seamlessly using Filecoin Pay.

Together, these components create a full-stack solution for the programmable, user-sovereign web that x402 envisions.

Key Questions for the Workshop

  1. How can Filecoin and IPFS position themselves as the storage and retrieval layer for x402 applications?
  1. How does Filecoin Pay compete with the speed and cost of solutions like Base?
  1. Can we differentiate by offering both storage and payment rails within the same network?
  1. Can content addressing, incremental verification, and streaming payments be the key features to drive new demand to Filecoin through x402?

Proposal

The key invention is a new kind of payment rail, where the retrieval client initially pre-authorises a small amount of funds (fixedLockup) and then creates signed payment tickets for individual retrievals. The client provides these tickets to the SP in the HTTP headers of each retrieval request. Later, the SP submits these tickets to the payment rail operator contract to unlock payment settlement.

Because each payment rail is associated with a single SP, and each payment ticket is bound to a payment rail, the SP does not need to wait for the blockchain to finalise the payment before they can be assured that the payment is valid. The SP can keep track of the remaining payment rail balance (fixedLockup) off-chain. There is zero risk that someone else will drain the fixed lockup, because only the SP has permission to do so.

Note: In case the retrieval client is the same as the storage client (i.e., I want to pay to retrieve my own data), the x402 rail can be created as part of the storage deal. For the “requester pays” case, the requester must create the payment rail before requesting the first piece.

Note:

A key assumption is that we're optimizing for a long-standing or at least repeated relationship between the payer and payee. Filecoin Pay rails are inherently stateful, and the initial setup cost only makes sense if it's amortized over many subsequent, high-frequency payments.

sequenceDiagram
	actor RC as Retrieval Client
	actor Pay as Filecoin Pay Rail
	actor SP as Storage Provider
	
	rect rgb(255,227,177)
	note left of RC: Client performs the initial payment setup
	RC ->> Pay: create x402 payment rail
	RC ->> Pay: add to fixed lockup
	SP ->> Pay: listen for lockup change
	SP ->> SP: update the off-chain cache of rail lockup balance
	end

  rect rgb(216,255,216)
  note left of RC: Client retrieves content from the SP
	loop
	RC ->> SP: GET /piece/bafk1
	SP ->> RC: 402 Payment Required (+ price)
	RC ->> RC: Sign a payment ticket
	RC ->> SP: GET /piece/bafk1 + ticket
	SP ->> SP: validate the ticket
	SP ->> SP: check the remaining lockup balance
	SP ->> SP: store the ticket for future settlement
	SP ->> SP: decrease the remaining lockup balance
	SP ->> RC: return bafk1 piece bytes
	end
	end


  rect rgb(177,177,255)
  note right of SP: SP periodically settles payments
  loop every X hours
  loop for each rail
  SP ->> SP: collect all payment tickets
  SP ->> SP: perform a ZK rollup
  SP ->> Pay: submit the ZK rollup
  Pay ->> Pay: verify the ZK rollup
  Pay ->> SP: release the funds
  end
  end
  end

Comparison to x402 on Base

Benefits:

  • The SP can verify the payment ticket locally, no need to wait for on-chain confirmation & finality.
  • There are no centralised components involved. Just the client, the provider, and the chain.
  • Micro-payments are aggregated into a rollup, so SP pays lower gas fees.
  • All money flows through Filecoin Pay.
  • FilBeam can use this mechanism to pay the SP for cache-miss requests in a trustless way. (As opposed to the current design, where the SP has to trust FilBeam to account for egress consumption correctly.)

What remains the same:

  • The client does not pay a gas fee for individual retrievals, only the one-time setup fee (once per SP).
  • SPs are entirely in control of pricing for retrievals; they can set their own egress charges.
  • There is no guarantee that the client will receive the content they paid for.

Drawbacks:

  • The retrieval client must set up a payment rail and lock funds, and this must be done for each SP from which they want to retrieve data.

Open questions & problems

  • How to prevent the SP from reusing payment tickets (double-charging the client).
    • I think we will need to use nonces. That will complicate both the client and the server, especially when multiple retrievals happen concurrently.
    • How does x402 handle this? More research is needed.
  • How to implement an efficient ZK rollup that can be efficiently verified on the chain by the Filecoin Pay operator.
    • In the worst case, or to reduce the scope of the MVP, we can omit rollups and let the SP submit an array of payment tickets in a single contract call (a single TX).
  • In this new design, the client can no longer pay for cache-miss retrievals directly; FilBeam needs to pay instead. Since SPs are in control of egress pricing, FilBeam needs to decide how much it is willing to pay.
    • How can FilBeam handle the case when the SP is charging for egress more than FilBeam is willing to serve?
    • Alternatively, we need to find a way to allow the client to provide the payment ticket for cache-miss retrievals, so that FilBeam can forward it to the SP.

What we need to build

  • A spec for the payment ticket format
    • This should be as closely aligned with x402 as possible
  • A new payment rail operator contract
    • The client (payer) can add funds to the fixed lockup
    • The SP (payee) can submit payment tickets signed by the client to release funds (settle payments)
  • Support in Curio
    • Sync the balance of x402 payment rail balances with the local state in DB.
    • x402 middleware for Piece & IPFS retrievals
    • A new cron job to submit payment tickets to Filecoin Pay
  • Support in Synapse SDK
    • Create the x402 payment rail
    • Retrieve data with a payment ticket
  • Support in FilBeam - cache-miss requests
    • Rework Synapse SDK withCDN dataset set up to use the new x402 payment operator for cache-miss payments.
    • FilBeam need to send a payment ticket with each cache-miss request.
  • Support in FilBeam - cache-hit requests
    • Rework Synapse SDK withCDN dataset set up to use the new x402 payment operator for CDN payments.
    • Rework FilBeam to use 402 to charge for retrievals
      • Validate payment tickets received in requests
      • Modify the usage reporter to submit payment tickets

Conversations

https://space-meridian.slack.com/archives/C07CGTXHHT4/p1762417174245479?thread_ts=1762264891.382259&cid=C07CGTXHHT4

Unidirectional State Channel (Signature-Based): This aligns closely with your idea. We could implement a simple unidirectional state channel managed by the payment rail contract.The contract would maintain a mapping like: mapping(address => mapping(address => mapping(address => Channel))) public channels; (Payer => Payee => Token => Channel)The retrieval client (payer) would then sign cumulative payment messages off-chain for a specific sessionId. The signed message would be the "ticket":

keccak256(abi.encodePacked(
    address(this), // Contract address
    payer,
    payee,
    channel.token,
    amount,          // Cumulative amount
    nonce,
    channel.sessionId
));

Benefits: The amount variable is cumulative. The Storage Provider only needs to validate and store the latest signed message with the highest amount for that session. This directly solves the "double-charging" problem you raised in your open questions. The SP can settle by submitting just this one signature. I wrote a similar 2-of-2 multisig implementation here (though it has a few known bugs, the concept is there).

As a simpler alternative, we could use a hash-based scheme (sample implementation)
Benefits: It's much simpler to validate off-chain. It's also useful in scenarios where you want to minimize security exposure (e.g., an agent doesn't need to store a private key, only pre-images).

Performance requirements from Curio

  • Support low latency, sub-ms, certainly sub-5ms overhead (compute on client and server + any backend calls)
  • Must support extremely small amounts, e.g. a $0.0000001 should be rational to accept (e.g. a payment for a 1MiB piece of data on a 10Gbit/s link that costs $1k/mo)
  • Not requiring an on-chain setup for new client-provider relations
  • Must support 1000s of parallel requests (very common in bulk object storage, which is coincidentally just about the only application where the "client pays" model is somewhat common in retrieval).

A benchmark for the usefulness of such a system:

  • Can it handle 100k retrievals per second (just ~10x more than what many providers see today)?
  • Is the provider-side overhead acceptable?
  • Can providers still scale the retrieval endpoint horizontally? (High-volume retrievals just can't be served from a single computer; eventually, that also applies to just processing payments, but we're not there yet.)
  • Are 1000s of parallel retrievals in a single client<->provider relation possible?